/*                                                                      
 Copyright 2018-2020 Nuclei System Technology, Inc.                
                                                                         
 Licensed under the Apache License, Version 2.0 (the "License");         
 you may not use this file except in compliance with the License.        
 You may obtain a copy of the License at                                 
                                                                         
     http://www.apache.org/licenses/LICENSE-2.0                          
                                                                         
  Unless required by applicable law or agreed to in writing, software    
 distributed under the License is distributed on an "AS IS" BASIS,       
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and     
 limitations under the License.                                          
 */

//=====================================================================
//
// Designer   : LZB
//
// Description:
//  The Module to realize a simple NICE core
//
// ====================================================================
`include "e203_defines.v"

`ifdef E203_HAS_NICE//{
module e203_subsys_nice_prg_core (
    // System	
    input                         nice_clk             ,
    input                         nice_rst_n	          ,
    output                        nice_active	      ,
    output                        nice_mem_holdup	  ,
//    output                        nice_rsp_err_irq	  ,
    // Control cmd_req
    input                         nice_req_valid       ,
    output                        nice_req_ready       ,
    input  [`E203_XLEN-1:0]       nice_req_inst        ,
    input  [`E203_XLEN-1:0]       nice_req_rs1         ,
    input  [`E203_XLEN-1:0]       nice_req_rs2         ,
    // Control cmd_rsp	
    output                        nice_rsp_valid       ,
    input                         nice_rsp_ready       ,
    output [`E203_XLEN-1:0]       nice_rsp_rdat        ,
    output                        nice_rsp_err    	  ,
    // Memory lsu_req	
    output                        nice_icb_cmd_valid   ,
    input                         nice_icb_cmd_ready   ,
    output [`E203_ADDR_SIZE-1:0]  nice_icb_cmd_addr    ,
    output                        nice_icb_cmd_read    ,
    output [`E203_XLEN-1:0]       nice_icb_cmd_wdata   ,
//    output [`E203_XLEN_MW-1:0]     nice_icb_cmd_wmask   ,  // 
    output [1:0]                  nice_icb_cmd_size    ,
    // Memory lsu_rsp	
    input                         nice_icb_rsp_valid   ,
    output                        nice_icb_rsp_ready   ,
    input  [`E203_XLEN-1:0]       nice_icb_rsp_rdata   ,
    input                         nice_icb_rsp_err	

);

//   localparam PIPE_NUM = 3;


// here we only use custom3: 
// CUSTOM0 = 7'h0b, R type
// CUSTOM1 = 7'h2b, R tpye
// CUSTOM2 = 7'h5b, R type
// CUSTOM3 = 7'h7b, R type

// RISC-V format  
//	.insn r  0x33,  0,  0, a0, a1, a2       0:  00c58533[ 	]+add [ 	]+a0,a1,a2
//	.insn i  0x13,  0, a0, a1, 13           4:  00d58513[ 	]+addi[ 	]+a0,a1,13
//	.insn i  0x67,  0, a0, 10(a1)           8:  00a58567[ 	]+jalr[ 	]+a0,10 (a1)
//	.insn s   0x3,  0, a0, 4(a1)            c:  00458503[ 	]+lb  [ 	]+a0,4(a1)
//	.insn sb 0x63,  0, a0, a1, target       10: feb508e3[ 	]+beq [ 	]+a0,a1,0 target
//	.insn sb 0x23,  0, a0, 4(a1)            14: 00a58223[ 	]+sb  [ 	]+a0,4(a1)
//	.insn u  0x37, a0, 0xfff                18: 00fff537[ 	]+lui [ 	]+a0,0xfff
//	.insn uj 0x6f, a0, target               1c: fe5ff56f[ 	]+jal [ 	]+a0,0 target
//	.insn ci 0x1, 0x0, a0, 4                20: 0511    [ 	]+addi[ 	]+a0,a0,4
//	.insn cr 0x2, 0x8, a0, a1               22: 852e    [ 	]+mv  [ 	]+a0,a1
//	.insn ciw 0x0, 0x0, a1, 1               24: 002c    [ 	]+addi[ 	]+a1,sp,8
//	.insn cb 0x1, 0x6, a1, target           26: dde9    [ 	]+beqz[ 	]+a1,0 target
//	.insn cj 0x1, 0x5, target               28: bfe1    [ 	]+j   [ 	]+0 targe

   ////////////////////////////////////////////////////////////
   // decode
   ////////////////////////////////////////////////////////////
   wire [6:0] opcode      = {7{nice_req_valid}} & nice_req_inst[6:0];
   wire [2:0] rv32_func3  = {3{nice_req_valid}} & nice_req_inst[14:12];
   wire [6:0] rv32_func7  = {7{nice_req_valid}} & nice_req_inst[31:25];

//   wire opcode_custom0 = (opcode == 7'b0001011); 
//   wire opcode_custom1 = (opcode == 7'b0101011); 
//   wire opcode_custom2 = (opcode == 7'b1011011); 
   wire opcode_custom3 = (opcode == 7'b1111011); 

   wire rv32_func3_000 = (rv32_func3 == 3'b000); 
   wire rv32_func3_001 = (rv32_func3 == 3'b001); 
   wire rv32_func3_010 = (rv32_func3 == 3'b010); 
   wire rv32_func3_011 = (rv32_func3 == 3'b011); 
   wire rv32_func3_100 = (rv32_func3 == 3'b100); 
   wire rv32_func3_101 = (rv32_func3 == 3'b101); 
   wire rv32_func3_110 = (rv32_func3 == 3'b110); 
   wire rv32_func3_111 = (rv32_func3 == 3'b111); 

   wire rv32_func7_0000000 = (rv32_func7 == 7'b0000000); 
   wire rv32_func7_0000001 = (rv32_func7 == 7'b0000001); 
   wire rv32_func7_0000010 = (rv32_func7 == 7'b0000010); 
   wire rv32_func7_0000011 = (rv32_func7 == 7'b0000011); 
   wire rv32_func7_0000100 = (rv32_func7 == 7'b0000100); 
   wire rv32_func7_0000101 = (rv32_func7 == 7'b0000101); 
   wire rv32_func7_0000110 = (rv32_func7 == 7'b0000110); 
   wire rv32_func7_0000111 = (rv32_func7 == 7'b0000111); 

   ////////////////////////////////////////////////////////////
   // custom3:
   // Supported format: only R type here
   // Supported instr:
   //  1. custom3 lbuf: load data(in memory) to row_buf
   //     lbuf (a1)
   //     .insn r opcode, func3, func7, rd, rs1, rs2    
   //  2. custom3 start: store data(in row_buf) to memory
   //     start (a1)
   //     .insn r opcode, func3, func7, rd, rs1, rs2    
   //  3. custom3 acc rowsum: load data from memory(@a1), accumulate row datas and write back 
   //     rowsum rd, a1, x0
   //     .insn r opcode, func3, func7, rd, rs1, rs2    
   ////////////////////////////////////////////////////////////
   wire custom3_rdstat     = opcode_custom3 & rv32_func3_110 & rv32_func7_0000101; 
   wire custom3_start     = opcode_custom3 & rv32_func3_111 & rv32_func7_0000110; 

   ////////////////////////////////////////////////////////////
   //  multi-cyc op 
   ////////////////////////////////////////////////////////////
   wire custom_multi_cyc_op = custom3_rdstat | custom3_start;
   // need access memory
   wire custom_mem_op = custom3_start;
   // need return value
   wire custom_ret_op = custom3_rdstat | custom3_start;
   
   ////////////////////////////////////////////////////////////
   //  rs register 
   ////////////////////////////////////////////////////////////
 
    wire [`E203_XLEN-1:0] rs1_r;
    wire rs1_ena, rs1_rst_n;
    sirv_gnrl_dfflr #(`E203_XLEN)   rs1_dfflr (rs1_ena, nice_req_rs1, rs1_r, nice_clk, rs1_rst_n);
    assign rs1_ena = nice_req_valid;
    assign rs1_rst_n = nice_rst_n;
    wire [`E203_XLEN-1:0] rs2_r;
    wire rs2_ena, rs2_rst_n;
    sirv_gnrl_dfflr #(`E203_XLEN)   rs2_dfflr (rs2_ena, nice_req_rs2, rs2_r, nice_clk, rs2_rst_n);
    assign rs2_ena = nice_req_valid;
    assign rs2_rst_n = nice_rst_n;

 
   ////////////////////////////////////////////////////////////
   // NICE FSM 
   ////////////////////////////////////////////////////////////
   parameter NICE_FSM_WIDTH = 3; 
   parameter IDLE          = 3'b000; 
   parameter RDSTAT        = 3'b100; 
   parameter START         = 3'b001; 

   wire [NICE_FSM_WIDTH-1:0] state_r; 
   wire [NICE_FSM_WIDTH-1:0] nxt_state; 
   wire [NICE_FSM_WIDTH-1:0] state_idle_nxt; 
   wire [NICE_FSM_WIDTH-1:0] state_rdstat_nxt; 
   wire [NICE_FSM_WIDTH-1:0] state_start_nxt; 

   wire nice_req_hsked;
   wire nice_rsp_hsked;
   wire nice_icb_rsp_hsked;
   wire illgel_instr = ~(custom_multi_cyc_op);

   wire state_idle_exit_ena; 
   wire state_rdstat_exit_ena; 
   wire state_start_exit_ena; 
   
   wire state_ena; 

   wire state_is_idle       = (state_r == IDLE); 
   wire state_is_rdstat     = (state_r == RDSTAT); 
   wire state_is_start      = (state_r == START); 

   assign state_idle_exit_ena = state_is_idle & nice_req_hsked & ~illgel_instr; 
   assign state_idle_nxt =  custom3_rdstat    ? RDSTAT   : 
                            custom3_start     ? START    :
			    IDLE;

   wire rdstat_done; 
   assign state_rdstat_exit_ena = state_is_rdstat & rdstat_done; 
   assign state_rdstat_nxt = IDLE;

   wire start_done; 
   assign state_start_exit_ena = state_is_start & start_done; 
   assign state_start_nxt = IDLE;


   assign nxt_state =   ({NICE_FSM_WIDTH{state_idle_exit_ena    }} & state_idle_nxt    )
                      | ({NICE_FSM_WIDTH{state_rdstat_exit_ena  }} & state_rdstat_nxt  )
                      | ({NICE_FSM_WIDTH{state_start_exit_ena   }} & state_start_nxt   ) 
                      ;

   assign state_ena =   state_idle_exit_ena | state_rdstat_exit_ena 
                      | state_start_exit_ena;

   sirv_gnrl_dfflr #(NICE_FSM_WIDTH)   state_dfflr (state_ena, nxt_state, state_r, nice_clk, nice_rst_n);

   ////////////////////////////////////////////////////////////
   // instr EXU
   ////////////////////////////////////////////////////////////

   //wire [COL_IDX_W-1:0]  rownum;

   //////////// 1. custom3_rdstat
   
   wire busy_status_r;
   wire busy_status_ena, busy_status_rst_n;
   sirv_gnrl_dfflr #(1)   busy_status_dfflr (busy_status_ena, 1'b1, busy_status_r, nice_clk, busy_status_rst_n);

   wire [`E203_XLEN-1:0] rdstat_res;
   wire nice_rsp_valid_rdstat;
   assign rdstat_res = state_is_rdstat ?  {30'b0,busy_status_r} 
                                       : 32'b0;
   assign nice_rsp_valid_rdstat = state_is_rdstat;
   assign rdstat_done = state_is_rdstat & nice_rsp_hsked;

   //////////// 2. custom3_start
   wire [`E203_XLEN-1:0] start_res;

          /////*************************************Control*******************************////      
           parameter START_FSM_WIDTH     = 3; 
           parameter START_IDLE          = 3'b000; 
           parameter START_LOAD          = 3'b100; 
           parameter START_ANS           = 3'b010; 
        
           wire [START_FSM_WIDTH-1:0] start_state_r; 
           wire [START_FSM_WIDTH-1:0] start_nxt_state; 
           wire [START_FSM_WIDTH-1:0] start_state_idle_nxt; 
           wire [START_FSM_WIDTH-1:0] start_state_load_nxt; 
           wire [START_FSM_WIDTH-1:0] start_state_ans_nxt; 
        
           wire start_state_idle_exit_ena; 
           wire start_state_load_exit_ena; 
           wire start_state_ans_exit_ena; 
           
           wire start_state_ena; 
        
           wire start_state_is_idle      = (start_state_r == START_IDLE); 
           wire start_state_is_load      = (start_state_r == START_LOAD); 
           wire start_state_is_ans       = (start_state_r == START_ANS); 
        
           wire [15-1:0] start_batch = 15'd12288; // 48kB
        
        
           assign start_state_idle_exit_ena = start_state_is_idle & state_is_start & ~busy_status_r; 
           assign start_state_idle_nxt =  START_ANS;
        
           wire start_load_done; 
           assign start_state_load_exit_ena = start_state_is_load & start_load_done; 
           assign start_state_load_nxt = START_IDLE;
           
           
           assign start_state_ans_exit_ena = start_state_is_ans; 
           assign start_state_ans_nxt = START_LOAD;
        
           assign start_nxt_state  = ({START_FSM_WIDTH{start_state_idle_exit_ena    }} & start_state_idle_nxt    )
                                   | ({START_FSM_WIDTH{start_state_load_exit_ena    }} & start_state_load_nxt    )
                                   | ({START_FSM_WIDTH{start_state_ans_exit_ena     }} & start_state_ans_nxt     ) 
                                   ;
        
           assign start_state_ena =   start_state_idle_exit_ena | start_state_load_exit_ena 
                                  |   start_state_ans_exit_ena;
        
           sirv_gnrl_dfflr #(START_FSM_WIDTH)   start_state_dfflr (start_state_ena, start_nxt_state, start_state_r, nice_clk, nice_rst_n);
           
       //////
   wire start_icb_cmd_hsked;
   wire nice_rsp_valid_start;
   wire nice_icb_cmd_hsked;
   
   assign start_icb_cmd_hsked = start_state_is_load  & nice_icb_cmd_hsked;

    reg [`E203_XLEN-1: 0] start_read_addr;
    reg [`E203_XLEN-1: 0] start_write_addr;
   always@(posedge nice_clk) begin
       if(~nice_rst_n) begin
           start_read_addr <= 0;
           start_write_addr <= 0;
       end
       else if(start_state_is_ans) begin
           start_read_addr   <= rs1_r;
           start_write_addr  <= rs2_r;
       end
   end


   wire [32-1:0] start_cmd_cnt_r; 
   wire [32-1:0] start_cmd_cnt_nxt; 
   wire start_cmd_cnt_clr;
   wire start_cmd_cnt_incr;
   wire start_cmd_cnt_ena;
   wire start_cmd_cnt_last;
   wire start_phase;
   wire start_phase_rst_n;
   wire start_phase_ena;
   wire start_icb_read;
   wire [`E203_XLEN-1: 0] start_addr_base;
   wire [`E203_XLEN-1: 0] start_icb_wdata;
   
   assign start_phase_rst_n = nice_rst_n & ~start_state_is_idle;
   assign start_icb_read = ~start_phase;
   assign start_addr_base = start_state_is_load ? start_phase ? start_read_addr : start_write_addr : 32'h0;
   sirv_gnrl_dfflr #(1)   start_phase_dfflr (start_phase_ena, ~start_phase, start_phase, nice_clk, start_phase_rst_n);
   
   assign start_cmd_cnt_last = (start_cmd_cnt_r == start_batch);
   assign start_cmd_cnt_clr = start_state_is_idle;
   assign start_cmd_cnt_incr = (start_icb_cmd_hsked & ~start_cmd_cnt_last & start_phase)
                             | (start_state_is_ans & start_state_ans_exit_ena);
   assign start_cmd_cnt_ena = start_cmd_cnt_clr | start_cmd_cnt_incr;
   assign start_cmd_cnt_nxt =   ({32{start_cmd_cnt_clr }} & {32{1'b0}})
                             | ({32{start_cmd_cnt_incr}} & (start_cmd_cnt_r + 1'b1) )
                             ;
   sirv_gnrl_dfflr #(32)   start_cmd_cnt_dfflr (start_cmd_cnt_ena, start_cmd_cnt_nxt, start_cmd_cnt_r, nice_clk, nice_rst_n);

   // nice_icb_cmd_valid sets when start_cmd_cnt_r is not full in start
   wire nice_icb_cmd_valid_start;

   
   assign start_load_done = start_cmd_cnt_ena & (start_cmd_cnt_r==start_batch-1); 
   assign start_res = {31'b0,start_state_idle_exit_ena};
   assign nice_rsp_valid_start = state_is_start;
   assign start_done = state_is_start & nice_rsp_hsked;

   
   ////////////
        // dtcm recieve data buffer 
        wire rcv_data_buf_ena;
        wire rcv_data_buf_set;
        wire rcv_data_buf_clr;
        wire rcv_data_buf_valid;
        wire [`E203_XLEN-1:0] rcv_data_buf; 
        wire [32-1:0] rcv_data_buf_idx; 
        wire [32-1:0] rcv_data_buf_idx_nxt; 
        wire start_icb_rsp_hsked = nice_icb_rsp_hsked & start_state_is_load;
//        assign rcv_data_buf_set = start_icb_rsp_hsked;
//        assign rcv_data_buf_clr = start_state_is_idle;
//        assign rcv_data_buf_ena = rcv_data_buf_clr | rcv_data_buf_set;
        
//        sirv_gnrl_dfflr #(1)   rcv_data_buf_valid_dfflr (1'b1, rcv_data_buf_ena, rcv_data_buf_valid, nice_clk, nice_rst_n);
//        sirv_gnrl_dfflr #(`E203_XLEN)   rcv_data_buf_dfflr (rcv_data_buf_ena, nice_icb_rsp_rdata, rcv_data_buf, nice_clk, nice_rst_n);
        assign start_phase_ena = (start_icb_cmd_hsked & ~start_cmd_cnt_last);
        assign nice_icb_cmd_valid_start = start_state_is_load & (start_cmd_cnt_r < start_batch);
        assign start_icb_wdata = nice_icb_rsp_rdata;
        assign busy_status_ena = start_state_is_load;
        assign busy_status_rst_n = ~start_state_load_exit_ena & nice_rst_n;
        //////////// mem aacess addr management
        wire [`E203_XLEN-1:0] maddr_acc_r; 
        assign nice_icb_cmd_hsked = nice_icb_cmd_valid & nice_icb_cmd_ready; 
        // custom3_rdstat
        //wire [`E203_XLEN-1:0] lbuf_maddr    = state_is_idle ? nice_req_rs1 : maddr_acc_r ; 
        wire rdstat_maddr_ena    =   1'b0;

        // custom3_start
        //wire [`E203_XLEN-1:0] start_maddr    = state_is_idle ? nice_req_rs1 : maddr_acc_r ; 
        wire start_maddr_ena     =  start_state_idle_exit_ena 
                                 | (start_state_is_load & nice_icb_cmd_hsked & start_phase)
                             ;


   // maddr acc 
   wire  maddr_ena = rdstat_maddr_ena | start_maddr_ena;
   wire maddr_acc_init = start_state_idle_exit_ena;
   wire [`E203_XLEN-1:0] maddr_acc_op1 = maddr_acc_init ? `E203_XLEN'h0 : maddr_acc_r; // not reused
   wire [`E203_XLEN-1:0] maddr_acc_op2 = maddr_acc_init ? `E203_XLEN'h0 : `E203_XLEN'h4; 

   wire [`E203_XLEN-1:0] maddr_acc_next = maddr_acc_op1 + maddr_acc_op2;
   wire  maddr_acc_ena = maddr_ena;

   sirv_gnrl_dfflr #(`E203_XLEN)   maddr_acc_dfflr (maddr_acc_ena, maddr_acc_next, maddr_acc_r, nice_clk, nice_rst_n);

   ////////////////////////////////////////////////////////////
   // Control cmd_req
   ////////////////////////////////////////////////////////////
   assign nice_req_hsked = nice_req_valid & nice_req_ready;
   assign nice_req_ready = state_is_idle & (custom_mem_op ? nice_icb_cmd_ready : 1'b1);

   ////////////////////////////////////////////////////////////
   // Control cmd_rsp
   ////////////////////////////////////////////////////////////
   assign nice_rsp_hsked = nice_rsp_valid & nice_rsp_ready; 
   assign nice_icb_rsp_hsked = nice_icb_rsp_valid & nice_icb_rsp_ready;
   assign nice_rsp_valid = nice_rsp_valid_rdstat | nice_rsp_valid_start;
   assign nice_rsp_rdat  = {`E203_XLEN{state_is_rdstat}} & rdstat_res
                         | {`E203_XLEN{state_is_start }} & start_res
                           ;
   assign nice_rsp_err   =   (nice_icb_rsp_hsked & nice_icb_rsp_err);

   ////////////////////////////////////////////////////////////
   // Memory lsu
   ////////////////////////////////////////////////////////////
   assign nice_icb_rsp_ready = 1'b1; 

   assign nice_icb_cmd_valid = nice_icb_cmd_valid_start;
   assign nice_icb_cmd_addr  = start_addr_base + maddr_acc_r;
   assign nice_icb_cmd_read  = start_state_is_load ? start_icb_read : 1'b1;
   assign nice_icb_cmd_wdata = start_state_is_load ? start_icb_wdata : 32'b0; 

   //assign nice_icb_cmd_wmask = {`sirv_XLEN_MW{custom3_start}} & 4'b1111;
   assign nice_icb_cmd_size  = 2'b10;
   assign nice_mem_holdup    =  ~start_state_is_idle; 

   ////////////////////////////////////////////////////////////
   // nice_active
   ////////////////////////////////////////////////////////////
   assign nice_active = state_is_idle ? nice_req_valid : 1'b1;

endmodule
`endif//}